package kr.or.smardi.smardi_AScan.Scan;

import java.nio.*;
import java.util.*;

import kr.or.smardi.smardi_AScan.*;
import kr.or.smardi.smardi_AScan.Device.*;
import kr.or.smardi.smardi_AScan.Graph.*;
import kr.or.smardi.smardi_AScan.Help.*;
import kr.or.smardi.smardi_AScan.Record.*;
import kr.or.smardi.smardi_AScan.User.*;
import kr.or.smardi.smardi_AScan.db.*;
import android.app.*;
import android.content.*;
import android.media.*;
import android.os.*;
import android.os.Handler.Callback;
import android.util.*;
import android.view.*;
import android.widget.*;

public class AC_Scan_process extends Activity {
	
	private boolean D = false;
	
	MyView graphView = null;

	TextView txt_explain = null;

	RelativeLayout titleBar = null;
	ImageButton btn_TopLeft = null;
	ImageButton btn_TopRight = null;
	TextView txt_subTitle = null;

	// 불어달라는 이미지
	ImageView overlay_blow = null;

	// 탭 메뉴
	LinearLayout tabMenu = null;
	ImageButton tab_measure = null;
	ImageButton tab_record = null;
	ImageButton tab_user = null;
	ImageButton tab_help = null;

	TextView btn_save = null;
	boolean isSaved = false; // 데이터를 저장했는지

	RelativeLayout layout_preheating = null; // 준비중 화면
	LinearLayout layout_resultControl = null;

	// 결과 숫자
	TextView resultNumber = null;

	double maxAlcohol = 0; // 결과 알콜 농도

	@SuppressWarnings("unused")
	private boolean isAbleToWriteData = false;

	// 녹음 관련된 변수
	AudioRecord recorder;
	
	private static final int RECORDER_SAMPLERATE = 8000;
	private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_STEREO;
	private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;

	private int bufferSize = 0;

	private Thread recordingThread = null;

	private boolean isRecording = false; // 현재 녹음 중인지 표시함

	public int buflen; // 2048 (really only need 480...)

	public static long t1, t2, dt;
	public static short[] buffer; // +-32767
	public float db;
	public int i, peak, avg, sum, samp;

	int oldAVG = 0;
	long oldTime = 0;

	int recordingValues = 0; // 측정되는 값
	int recordingTangent = 0; // 측정되는 값의 순간 기울기

	long time_RecordingStart = 0;

	// 기기 설정을 가져오기 위해..
	private PhoneBoundary phoneBoundary = null;
	private LookupTable lookupTable = null;

	// 나가기
	long lastBackPressTime = 0;
	Toast toast = null;
	boolean isUserWantToExit = false;

	// Database
	DB_Adapter mDb_Adapter;

	// SharedPreference
	Manage_SharedPreference mPref;

	/**
	 * Epi가 꽂힌 상태를 나타내기 위한 변수 headSetState 0: 뽑혔을 때 1: 꽂혀있을 때
	 */
	int headSetState = 0;
	int hasMicrophone = 0;

	// 불어주세요 화면의 이미지를 저장할 리스트
	ArrayList<Integer> list_blowImage = null;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.ac_scan_process);

		phoneBoundary = new PhoneBoundary(Build.MODEL);
		lookupTable = new LookupTable(Build.MODEL);

		graphView = (MyView) findViewById(R.id.graphView);

		txt_explain = (TextView) findViewById(R.id.txt_explain);

		btn_save = (TextView) findViewById(R.id.btn_save);
		btn_save.setOnClickListener(onClickListener);

		resultNumber = (TextView) findViewById(R.id.resultNumber);

		titleBar = (RelativeLayout) findViewById(R.id.inc_title_bar);
		btn_TopLeft = (ImageButton) titleBar.findViewById(R.id.btn_top_left);
		btn_TopLeft.setImageDrawable(getResources().getDrawable(
				R.drawable.c_btn_back));
		btn_TopLeft.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				goBack();
			}
		});
		btn_TopRight = (ImageButton) titleBar.findViewById(R.id.btn_top_right);
		btn_TopRight.setVisibility(View.GONE);
		txt_subTitle = (TextView) findViewById(R.id.txt_subTitle);
		txt_subTitle.setText(getString(R.string.subTitle_scan_process));

		// 불어달라는 이미지
		overlay_blow = (ImageView) findViewById(R.id.overlay_blow);
		list_blowImage = new ArrayList<Integer>();
		if (getString(R.string.language).equals("en")) {
			list_blowImage.add(R.drawable.blow_0_en);
			list_blowImage.add(R.drawable.blow_1_en);
		} else {
			list_blowImage.add(R.drawable.blow_0);
			list_blowImage.add(R.drawable.blow_1);
		}

		// 탭 세팅--------------------------
		tabMenu = (LinearLayout) findViewById(R.id.inc_tab_menu);
		tab_measure = (ImageButton) findViewById(R.id.tab_measure);
		tab_measure.setImageDrawable(getResources().getDrawable(
				R.drawable.tab_measure_1));
		tab_record = (ImageButton) findViewById(R.id.tab_record);
		tab_user = (ImageButton) findViewById(R.id.tab_user);
		tab_help = (ImageButton) findViewById(R.id.tab_help);

		tab_record.setOnClickListener(onClickListener);
		tab_user.setOnClickListener(onClickListener);
		tab_help.setOnClickListener(onClickListener);
		// 탭 세팅 끝--------------------------

		mDb_Adapter = new DB_Adapter(AC_Scan_process.this);
		mDb_Adapter.open();

		mPref = new Manage_SharedPreference(AC_Scan_process.this);

		layout_preheating = (RelativeLayout) findViewById(R.id.layout_preheating);
		layout_resultControl = (LinearLayout) findViewById(R.id.layout_resultControl);

		// BraodcastReceiver
		IntentFilter filter = new IntentFilter();
		filter.addAction(Intent.ACTION_HEADSET_PLUG);
		registerReceiver(mBroadcastReceiver, filter);

		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					if (graphView.graph != null) {

						isAbleToWriteData = true;

						// 그래프의 바운더리를 설정
						long now = new Date().getTime();
						graphView.setDataBoundary(now, now + 30000, 0,
								phoneBoundary.getMaxValue());

						// 그래프에 룩업테이블 인덱스 전달
						graphView.setLookupTable(lookupTable);

						time_RecordingStart = now;

						// 녹음 관련 설정
						bufferSize = AudioRecord.getMinBufferSize(
								RECORDER_SAMPLERATE, RECORDER_CHANNELS,
								RECORDER_AUDIO_ENCODING);
						buffer = new short[bufferSize]; // 4096 is min on ion,
														// but
														// use 4800
						buflen = bufferSize / 2;

						startRecording();
						// graphView.addGraphData(count, (float)Math.random() *
						// 3000);
						break;
					} else {
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}
		});
		thread.start();

		// cTimer.start();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();

		stopRecording();
		unregisterReceiver(mBroadcastReceiver);
		mDb_Adapter.close();
	}

	View.OnClickListener onClickListener = new View.OnClickListener() {
		
		Intent intent = null;
		@Override
		public void onClick(View v) {
			switch (v.getId()) {
			case R.id.btn_save:
				if (isSaved == false) {
					isSaved = true;
					// btn_save.setBackgroundDrawable(getResources().getDrawable(R.drawable.c_btn_start_ascan));
					btn_save.setText("Saved");
					btn_save.setEnabled(false);

					long uid = mDb_Adapter.addRecord(maxAlcohol,
							mPref.getUserUID());
				}
				break;
			case R.id.tab_record:
				intent = new Intent(AC_Scan_process.this, AC_RecordMain.class);
				startActivity(intent);
				finish();
				overridePendingTransition(R.anim.fadein, R.anim.fadeout);
				break;
			case R.id.tab_user:
				intent = new Intent(AC_Scan_process.this, AC_UserMain.class);
				startActivity(intent);
				finish();
				overridePendingTransition(R.anim.fadein, R.anim.fadeout);
				break;
			case R.id.tab_help:
				intent = new Intent(AC_Scan_process.this, AC_Help_main.class);
				startActivity(intent);
				finish();
				overridePendingTransition(R.anim.fadein, R.anim.fadeout);
				break;
			}
		}
	};

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {

		switch (keyCode) {
		case KeyEvent.KEYCODE_BACK:
			return goBack();
		}

		return super.onKeyDown(keyCode, event);
	}

	private boolean goBack() {
		if (graphView.graph.state_ascan != Graph.STATE_ASCAN_END) {
			AlertDialog.Builder alert = new AlertDialog.Builder(
					AC_Scan_process.this);
			alert.setTitle(getString(R.string.dialog_goback_title));
			alert.setMessage(getString(R.string.dialog_goback_message));
			alert.setPositiveButton(getString(R.string.dialog_back),
					new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
							startActivity(new Intent(AC_Scan_process.this,
									AC_Scan_main.class));
							finish();
						}
					});
			alert.setNegativeButton(getString(R.string.dialog_Cancel),
					new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
							dialog.dismiss();
						}
					});
			alert.show();

			return false;
		} else {
			if (this.lastBackPressTime < System.currentTimeMillis() - 2000) {
				toast = Toast.makeText(this,
						getResources()
								.getString(R.string.toast_move_to_preview),
						2000);
				toast.show();
				this.lastBackPressTime = System.currentTimeMillis();
				return false;
			} else {
				if (toast != null) {
					toast.cancel();
				}

				isUserWantToExit = true;
				finish();
				startActivity(new Intent(AC_Scan_process.this,
						AC_Scan_main.class));
			}

			return true;
		}
	}

	private void stopRecording() {
		if (isRecording == true) {
			isRecording = false;
			recorder.stop();
			recorder.release();
		}
	}

	Handler mHandler = new Handler(new Callback() {
		long time_PrePreHeatingStart = 0; // 0.01 밑으로 내려간 시간
		long time_PreHeatingStart = 0; // 0.0075 밑으로 내려간 시간
		int newMeasuredValue = 0;
		int oldMeasuredValue = 0;
		long newMeasuredTime = 0;
		long oldMeasuredTime = 0;

		// 측정됐을 때의 최대값을 저장하는 변수

		long maxAlcoholTime = 0;

		@Override
		public boolean handleMessage(Message msg) {

			if (msg.what == 0) {
				long now = new Date().getTime();

				newMeasuredValue = Integer.parseInt(msg.obj.toString());
				newMeasuredTime = now;

				graphView.addGraphData(now, newMeasuredValue);

				// ----예열이 끝났는지 확인한다.
				double alcohol = lookupTable
						.convertVolumeToAlchole(newMeasuredValue);

				if(D) {
					Log.e("A-Scan", "Alcohol:"+alcohol);
				}
				
				switch (graphView.graph.state_ascan) {
				// 예열 중 일 때
				case Graph.STATE_ASCAN_PREHEATING:
					if (alcohol < 0.01) {
						if (time_PrePreHeatingStart == 0) {
							time_PrePreHeatingStart = now;
						} else {
							// 0.01밑으로 10초 이상 유지되면
							if (now - time_PrePreHeatingStart > 10000) {
								preheating_completed();
							}
						}

						if (alcohol < 0.0075) {
							if (time_PreHeatingStart == 0) {
								time_PreHeatingStart = now;
							} else {
								// 예열이 2초 이상 지속되면
								if (now - time_PreHeatingStart > 4000) {
									// 예열 완료!
									preheating_completed();
								}
							}
						} else {
							time_PreHeatingStart = 0;
						}
					} else {
						time_PrePreHeatingStart = now;
					}
					break;
				// 예열이 완료되어 측정 중 일 때
				case Graph.STATE_ASCAN_SCANNING:
					if (newMeasuredTime - oldMeasuredTime != 0) {
						// 기울기를 계산.
						double ratio = (double) (newMeasuredValue - oldMeasuredValue)
								/ (double) (newMeasuredTime - oldMeasuredTime);
						if (ratio > 5) {
							graphView.graph.state_ascan = Graph.STATE_ASCAN_DETECTING;

							txt_subTitle
									.setText(getString(R.string.scanProcess_title_detecting));
							txt_explain
									.setText(getString(R.string.scanProcess_explain_detecting));
						}
					}
					break;
				// 입김이 감지됐을 때
				case Graph.STATE_ASCAN_DETECTING:
					overlay_blow.setVisibility(View.GONE);
					cTimerBlow.cancel();

					if (maxAlcohol < alcohol) {
						maxAlcohol = alcohol;
						maxAlcoholTime = now;
					}

					if (now - maxAlcoholTime > 5000) {
						graphView.graph.state_ascan = Graph.STATE_ASCAN_END;

						txt_subTitle
								.setText(getString(R.string.scanProcess_title_complete));
						txt_explain
								.setText(getString(R.string.scanProcess_explain_complete));
					}
					break;
				// 측정이 끝났을 때
				case Graph.STATE_ASCAN_END:
					layout_resultControl.setVisibility(View.VISIBLE);
					txt_explain.setVisibility(View.GONE);
					if (maxAlcohol < 0.01) {
						maxAlcohol = 0;
					}
					resultNumber.setText(String.format("%.3f", maxAlcohol));
					// Toast.makeText(AC_Scan_process.this,
					// "측정결과:"+maxAlcohol+"%", 1000).show();
					stopRecording();
					break;
				// 에러가 발생했을 때
				case Graph.STATE_ASCAN_ERROR:

					break;

				}

				oldMeasuredValue = newMeasuredValue;
				oldMeasuredTime = newMeasuredTime;
			}

			return false;
		}
	});

	protected void preheating_completed() {
		graphView.graph.state_ascan = Graph.STATE_ASCAN_SCANNING;

		layout_preheating.setVisibility(View.GONE);
		txt_subTitle.setText(getString(R.string.scanProcess_title_scanning));
		txt_explain.setText(getString(R.string.scanProcess_explain_scanning));
		overlay_blow.setVisibility(View.VISIBLE);
		cTimerBlow.start();
	}

	// 불어주세요를 깜박이게
	CountDownTimer cTimerBlow = new CountDownTimer(Long.MAX_VALUE, 600) {

		int count = 0;

		@Override
		public void onTick(long millisUntilFinished) {

			if (count % 2 == 0) {
				overlay_blow.setImageDrawable(getResources().getDrawable(
						list_blowImage.get(0)));
			} else {
				overlay_blow.setImageDrawable(getResources().getDrawable(
						list_blowImage.get(1)));
			}

			count++;
		}

		@Override
		public void onFinish() {
			// 아무것도 하지마...
		}
	};

	// ----------------------------------------------------------------------------
	// --------------------------------------------------------------
	// 마이크로부터 신호 얻는 부분
	private void startRecording() {
		recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
				RECORDER_SAMPLERATE, RECORDER_CHANNELS,
				RECORDER_AUDIO_ENCODING, bufferSize);

		recorder.startRecording();

		isRecording = true;

		recordingThread = new Thread(new Runnable() {

			@Override
			public void run() {
				writeAudioDataToFile();
			}
		}, "AudioRecorder Thread");

		recordingThread.start();
	}

	private void writeAudioDataToFile() {
		byte data[] = new byte[bufferSize];

		int read = 0;

		while (isRecording) {
			read = recorder.read(data, 0, bufferSize);

			ByteBuffer byteBuffer = ByteBuffer.wrap(data);
			ShortBuffer shorts = byteBuffer.asShortBuffer();

			buffer = new short[shorts.remaining()];

			for (int i = 0, n = shorts.remaining(); i < n; ++i) {
				// buffer[i] = (short)((data[2*i] << 8) | (data[2*i + 1] &
				// 0xff));

				buffer[i] = byteArrayToShort(data, 2 * i, false);
			}

			// int mSamplesRead = recorder.read(buffer, 0, bufferSize);

			if (AudioRecord.ERROR_INVALID_OPERATION != read) {
				calcamplitude();
			}
		}
	}

	public static short byteArrayToShort(byte[] paRawBytes, int piOffset,
			boolean pbBigEndian) {
		int iRetVal = -1;

		// we ingnore excessing bytes?
		if (paRawBytes.length < piOffset + 2)
			return -1;

		int iLow;
		int iHigh;

		if (pbBigEndian) {
			iLow = paRawBytes[piOffset + 1];
			iHigh = paRawBytes[piOffset + 0];
		} else {
			iLow = paRawBytes[piOffset + 0];
			iHigh = paRawBytes[piOffset + 1];
		}

		// Merge high-order and low-order byte to form a 16-bit double value.
		iRetVal = (iHigh << 8) | (0xFF & iLow);

		return (short) iRetVal;
	}

	void calcamplitude() {
		try {
			sum = 0;
			peak = 0;
			for (i = 0; i < buflen; i++) {
				samp = Math.abs(buffer[i]);
				if (samp > peak)
					peak = samp;
				sum += samp;
			}
			avg = sum / buflen;
			// db = (float) (20.0 * Math.log10(avg / 32767.0));

			Message msg = new Message();
			msg.what = 0;
			// msg.obj = "value=" + avg + "\n" + "db:" + String.valueOf(db);
			msg.obj = String.valueOf(peak);
			mHandler.sendMessage(msg);
		} catch (Exception e) {

		}
	}

	// -------------------------------------------------------
	private void checkConnection() {
		// AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
		// Log.i("smardi.Epi", "Wire connection:" + am.isWiredHeadsetOn());

		if (headSetState == 1 && hasMicrophone == 1) {

		} else {
			try {
				if (graphView.graph.state_ascan != Graph.STATE_ASCAN_END) {
					stopRecording();
					AlertDialog.Builder alert = new AlertDialog.Builder(
							AC_Scan_process.this);
					alert.setTitle(getString(R.string.dialog_connection_error_title));
					alert.setMessage(getString(R.string.dialog_connection_error_message));
					alert.setNegativeButton(getString(R.string.dialog_OK),
							new DialogInterface.OnClickListener() {
								@Override
								public void onClick(DialogInterface dialog,
										int which) {
									dialog.dismiss();
									finish();
									startActivity(new Intent(
											AC_Scan_process.this,
											AC_Scan_main.class));
								}
							});
					alert.setOnCancelListener(new DialogInterface.OnCancelListener() {
						@Override
						public void onCancel(DialogInterface dialog) {
							dialog.dismiss();
							finish();
							startActivity(new Intent(AC_Scan_process.this,
									AC_Scan_main.class));
						}
					});
					alert.show();
				}
			} catch (Exception e) {
				Log.e("A-Scan",
						"Error in checkConnection():" + e.getLocalizedMessage());
			}
		}
	}

	BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();

			if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
				/**
				 * headSetState 0: 뽑혔을 때 1: 꽂혀있을 때
				 */

				headSetState = intent.getIntExtra("state", 0);
				hasMicrophone = intent.getIntExtra("microphone", 0);
				// Log.e("Epi", "MIC:" + hasMicrophone);
				checkConnection();

			}
		}
	};
}
